/**
 * @brief A library for setting up the ADC and reading samples.
 * @file adc.c
 *
 * The function init_adc must be called at the beginning to configure
 * the ADC for single sample mode.
 *
 * The function get_adc_value can be called to perform a conversion.
 *
 * @author Zakkai Kauffman-Rogoff and Peter Kalauskas
 * @date Jan 23, 2009
 * @version 1
 *
 */

#include "adc.h"
#include "device.h"
#include <avr/io.h>
#include <stdlib.h>
#include "../communicator.h"

/**
 * @fn void init_adc(void)
 * @brief Configures the ADC for single sample mode and disables digital input
 * on PIN A0.
 * @return void
 */
void init_adc(void) {
        ADCSRA &= ~(1 << ADATE); // select single sample mode
        ADCSRA |= (1 << ADEN); // enable ADC hardware

        DDRA &= ~((1 << PA7) | (1 << PA6) | (1 << PA5) | (1 << PA4) | (1 << PA3)
                        | (1 << PA2) | (1 << PA1) | (1 << PA0));
        // Disable all digital input on PINA1/ADC1 and PINA0/ADC0
        DIDR0 |= (1 << ADC7D) | (1 << ADC6D) | (1 << ADC5D) | (1 << ADC4D) | (1
                        << ADC3D) | (1 << ADC2D) | (1 << ADC1D) | (1 << ADC0D);
}

/**
 * @fn uint8_t send_adc_value(uint8_t adc_channel)
 * @brief Starts an ADC conversion and sends the value to the client.
 *
 * @param adc_id The ADC channel to take a reading on, either 0 or 1.
 * @return uint16_t OK
 */
void send_adc_value(uint8_t adc_id, uint8_t *data, uint8_t length) {
        uint8_t low_byte, high_byte;
        ADMUX = adc_id;
        while (ADMUX != adc_id) {
                // wait for ADMUX to change
        }

        ADCSRA |= (1 << ADSC); // start conversion
        while ((ADCSRA & (1 << ADIF)) == 0) {
                // wait
        }

        low_byte = ADCL; // Read ADCL first .
        high_byte = ADCH;
        ADCSRA |= (1 << ADIF); // clear the conversion complete flag

        // adc_value = (uint16_t) (high_byte << 8) + low_byte
        uint8_t response[] = { high_byte, low_byte };
        send_command(ANALOG_DIGITAL_CONVERTER, adc_id, SET_VALUE, response, 2);
}

/**
 * @fn uint16_t get_adc_value(uint8_t adc_channel)
 * @brief Starts an ADC conversion and returns the value.
 *
 * @param adc_channel The ADC channel to take a reading on, either 0 or 1.
 * @return uint16_t ADC value.
 */
uint16_t get_adc_value(uint8_t adc_channel) {
	uint8_t low_byte, high_byte;
	ADMUX = adc_channel;
	while (ADMUX != adc_channel) {
		// wait for ADMUX to change
	}

	ADCSRA |= (1 << ADSC); // start conversion
	while ((ADCSRA & (1 << ADIF)) == 0) {
		// wait
	}

	low_byte = ADCL; // Read ADCL first .
	high_byte = ADCH;
	ADCSRA |= (1 << ADIF); // clear the conversion complete flag

	return (uint16_t) (high_byte << 8) + low_byte;
}
